home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / apps / database / postgres / postgre4.z / postgre4 / src / utils / fmgr / assoc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-27  |  17.3 KB  |  752 lines

  1. #include <ctype.h>
  2. #include <stdio.h>
  3.  
  4.  
  5. #define PANIC_FILE stderr
  6.  
  7. #include "tmp/c.h"
  8. #include "utils/log.h"
  9.     
  10. #include "assoc.h"
  11.  
  12. int* H_getmem();
  13.  
  14.  
  15. #define LOWER(ch) (isupper(ch)? tolower(ch) : (ch))
  16.  
  17. /************************************************************************\
  18. **************************************************************************
  19. **                                    **
  20. **  This package uses hash tables to implement an associative memory,    **
  21. **  or "name table".  See also "assoc.h" and "assoc_internals.h".    **
  22. **                                    **
  23. **  The user may associate names, also called "index strings" with    **
  24. **  packets of memory, called "mem_cell"s, which come in fixed sizes.    **
  25. **  Then if you know the name, you can look up the mem_cell or vice    **
  26. **  versa.                                **
  27. **                                    **
  28. **  The collision recovery mechanism is a nifty little scheme           **
  29. **  of my own invention, which I call "linear-congruential rehash",    **
  30. **  which means "add one and multiply by three".            **
  31. **                                    **
  32. **  The orbit of the rehash function touches exactly half the entries    **
  33. **  in the table, and the table is never allowed to be over half full,  **
  34. **  so there will always be an empty slot for a new entry.        **
  35. **  Removing entries is a little bit tricky. Since the lookup mechanism **
  36. **  stops and reports failure when it comes to an empty slot in the     **
  37. **  rehash orbit for the value searched for, when an entry is removed,    **
  38. **  the values in the same orbit which are there as a result of a     **
  39. **  collision must be backed up to fill the evacuated slot.          **
  40. **  The entry number is also there for removing entries.  It provides a **
  41. **  reference back to the slot which points to the entry.        **
  42. **                                                                      **
  43. **  We assume that our machine uses two's complement arithmetic.    **
  44. **  $Header: /private/postgres/src/utils/fmgr/RCS/assoc.c,v 1.8 1991/11/09 01:53:44 mer Exp $                                **
  45. **************************************************************************
  46. \************************************************************************/
  47.  
  48. /**************************************************************************
  49. **
  50. **  An entry looks like this:
  51. **
  52. **
  53. **  [pointers]      [pointees]
  54. **
  55. **          ------------------
  56. **  mem         -> |  entry number  |  index of entry into hash table.
  57. **          ------------------
  58. **  mem_cell ->    |             |
  59. **  (slot)    |         |
  60. **        | user's goodies |  of size table->value_size
  61. **            |         |
  62. **        ------------------
  63. **  string   -> |         |
  64. **        |  index string  |
  65. **        |         |
  66. **        |         |
  67. **        ------------------
  68. **
  69. **
  70. **  See string_from_cell(), cell_from_string(), mem_from_cell(), and
  71. **  cell_from_mem().. all macros.
  72. **
  73. \*************************************************************************/
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81. /* N.B. The routine assoc() depends on the fact that if a table is less
  82. ** than half full, the rehash orbit will always find an empty slot.
  83. ** This rehash will hit exactly half the slots before it repeats, provided
  84. ** that the table length is a power of two.
  85. */
  86. #define REHASH(hash,table) (((hash+1)*3) & table -> mask)
  87.  
  88.  
  89.  
  90.  
  91. /**** factory-procedure, makes new tables. ****/
  92.  
  93. assoc_mem
  94. new_assoc_mem(value_size)
  95.   int value_size; /* storage size of values to be in assoc mem */
  96. {
  97.   register assoc_mem table;
  98.  
  99.   table = (assoc_mem) H_getmem(sizeof (struct assoc_mem_rec));
  100.  
  101.   table->value_size = value_size;
  102.   table->entries = 0;
  103.   table->size = INIT_TABLE_SIZE;
  104.   table->size_div_2 = table->size / 2;
  105.   table->mask = table->size -1;
  106.   table->array = (hash_table *)H_getmem(table->size * (sizeof (mem_cell)));
  107.  
  108.   return table;
  109. }
  110.  
  111.  
  112.  
  113. /* Associates a name with a new mem_cell, or return pointers to previously
  114. ** associated cell.  Initializes new cells to all zero, so you may determine 
  115. ** whether or not a cell is new by smudging a "virgin-bit" when you
  116. ** first obtain a new cell.  If you get a cell from assoc() with a
  117. ** fresh new virgin bit, then the cell must be a new one.
  118. */
  119.  
  120.  
  121. mem_cell
  122. assoc( string, table)
  123.   register char* string;
  124.   register assoc_mem table;
  125. { /* assoc */
  126.  
  127.   int length;
  128.   int hashval;
  129.  
  130.   register int   rehash;
  131.   register entry assoc_value;
  132.  
  133.   /* This is essential.  See assoc_internals.h */
  134.   if (table->entries >= table->size_div_2 - 1)
  135.     H_expand_table(table);
  136.  
  137.   hash(string, table->mask, &length, &hashval);
  138.   rehash = hashval;
  139.  
  140.  look_at_slot:
  141.   {  register entry * slot = &((*(table->array))[rehash]);
  142.  
  143.      assoc_value = *slot;
  144.  
  145.      if ( (assoc_value) == 0)
  146.        /* Nothing else has hashed to this slot. 
  147.       We will put our string here. */
  148.        { 
  149.      *slot =  cell_from_mem(
  150.             (entry) H_getmem(table->value_size + length + sizeof('\0')
  151.                      + sizeof(entry*)));
  152.  
  153.      *(mem_from_cell(*slot)) = rehash;
  154.      assoc_value = *slot;
  155.      strcpy(string_from_cell(assoc_value, table), string);
  156.      table->entries++;
  157.      return assoc_value;        /* <=========== */
  158.        }
  159.     
  160.      if (strcmp( string_from_cell(assoc_value,table), string) != 0)
  161.        {                /* Oops.. collision. */
  162.      rehash = REHASH(rehash,table);
  163.      goto look_at_slot;        /* <=========== */
  164.        }
  165.      else return assoc_value;        /* <=========== */
  166.  
  167.    }
  168.     
  169.  
  170.   
  171.  
  172.  
  173. }                    /* end assoc */
  174.  
  175. /* Like assoc, except for non-null-terminated strings */
  176. mem_cell
  177. assocn( string, length, table)
  178.   register char* string;
  179.   register assoc_mem table;
  180.   register int length;
  181. { /* assocn */
  182.  
  183.   int hashval;
  184.  
  185.   register int   rehash;
  186.   register entry assoc_value;
  187.  
  188.   /* This is essential.  See assoc_internals.h */
  189.   if (table->entries >= table->size_div_2 - 1)
  190.     H_expand_table(table);
  191.  
  192.   hashn(string, table->mask, length, &hashval);
  193.   rehash = hashval;
  194.  
  195.  look_at_slot:
  196.   {  register entry * slot = &((*(table->array))[rehash]);
  197.  
  198.      assoc_value = *slot;
  199.  
  200.      if ( (assoc_value) == 0)
  201.        /* Nothing else has hashed to this slot. 
  202.       We will put our string here. */
  203.        { 
  204.      *slot =  cell_from_mem(
  205.             (entry) H_getmem(table->value_size + length + sizeof('\0')
  206.                      + sizeof(entry*)));
  207.  
  208.      *(mem_from_cell(*slot)) = rehash;
  209.      assoc_value = *slot;
  210.      strncpy(string_from_cell(assoc_value, table), string, length);
  211.      table->entries++;
  212.      return assoc_value;        /* <=========== */
  213.        }
  214.     
  215.      if (lstrncmp( string_from_cell(assoc_value,table), string, length) != 0)
  216.        {                /* Oops.. collision. */
  217.      rehash = REHASH(rehash,table);
  218.      goto look_at_slot;        /* <=========== */
  219.        }
  220.      else return assoc_value;        /* <=========== */
  221.  
  222.    }
  223.     
  224.  
  225. }                    /* end assocn */
  226. /* Like assoc, except for non-null-terminated strings, and folds cases. */
  227. mem_cell
  228. assocnf( string, length, table)
  229.   register char* string;
  230.   register assoc_mem table;
  231.   register int length;
  232. { /* assocn */
  233.  
  234.   int hashval;
  235.  
  236.   register int   rehash;
  237.   register entry assoc_value;
  238.  
  239.   /* This is essential.  See assoc_internals.h */
  240.   if (table->entries >= table->size_div_2 - 1)
  241.     H_expand_table(table);
  242.  
  243.   hashnf(string, table->mask, length, &hashval);
  244.   rehash = hashval;
  245.  
  246.  look_at_slot:
  247.   {  register entry * slot = &((*(table->array))[rehash]);
  248.  
  249.      assoc_value = *slot;
  250.  
  251.      if ( (assoc_value) == 0)
  252.        /* Nothing else has hashed to this slot. 
  253.       We will put our string here. */
  254.        { 
  255.      *slot =  cell_from_mem(
  256.             (entry) H_getmem(table->value_size + length + sizeof('\0')
  257.                      + sizeof(entry*)));
  258.  
  259.      *(mem_from_cell(*slot)) = rehash;
  260.      assoc_value = *slot;
  261.      strncpy(string_from_cell(assoc_value, table), string, length);
  262.      table->entries++;
  263.      return assoc_value;        /* <=========== */
  264.        }
  265.     
  266.      if (lstrncmpf( string_from_cell(assoc_value,table), string, length) != 0)
  267.        {                /* Oops.. collision. */
  268.      rehash = REHASH(rehash,table);
  269.      goto look_at_slot;        /* <=========== */
  270.        }
  271.      else return assoc_value;        /* <=========== */
  272.  
  273.    }
  274.     
  275. }                    /* end assocnf */
  276.  
  277. /* returns 0 iff a null-terminated string and counted string compare */
  278. static
  279. lstrncmp(nullterm, counted, len)
  280.   char* nullterm;
  281.   char* counted;
  282. {
  283.                 
  284.   while( *nullterm && len && *nullterm++ == *counted++)
  285.     len--;
  286.   return !(len == 0 && *nullterm == 0);
  287. }
  288.  
  289. /* returns 0 iff a null-terminated string and counted string compare */
  290. /* folds cases */
  291. static
  292. lstrncmpf(nullterm, counted, len)
  293.   char* nullterm;
  294.   char* counted;
  295. {
  296.                 
  297.   while( *nullterm && len && LOWER(*nullterm) == LOWER(*counted))
  298.     {
  299.       len--;
  300.       nullterm++; counted++;
  301.     }
  302.   return !(len == 0 && *nullterm == 0);
  303. }
  304.  
  305.  
  306.  
  307. /* Look up the mem_cell associated with a given name. */
  308. /* Returns NULL if not found.                  */
  309.  
  310. mem_cell
  311. assoc_lookup( string, table)
  312.   register char* string;
  313.   register assoc_mem table;
  314. {
  315.   register entry retval;
  316.   int length;
  317.   int hashval;
  318.  
  319.   if (table == (assoc_mem) NULL) {
  320.      elog(NOTICE, "assoc_lookup for %s called with NULL assoc_mem table.", string);
  321.      return ((mem_cell) NULL);
  322.   }
  323.  
  324.   hash(string, table->mask, &length, &hashval);
  325.  
  326.   { register int  rehash = hashval;
  327.     register int * assoc_value = 0;
  328.  
  329.     look_at_slot:
  330.     {  entry * slot = &((*(table->array))[rehash]);
  331.  
  332.        assoc_value = *slot;
  333.  
  334.        if ( (assoc_value) == 0)
  335.         /* Nothing has hashed to this slot. 
  336.            Lookup has come to a sorry end. */
  337.         { 
  338.            return assoc_value;  /* <=========== */
  339.         }
  340.     
  341.        if (strcmp( string_from_cell(assoc_value,table), string) == 0)
  342.         /*  An identical string was previously put in this slot.
  343.         **  We found it!
  344.         */
  345.             { 
  346.            return assoc_value;  /* <=========== */
  347.         }
  348.  
  349.        /* collision... try next slot in the rehash orbit */
  350.        rehash = REHASH(rehash,table);
  351.        goto look_at_slot;        /* <=========== */
  352.     }
  353.  
  354.   }
  355.  
  356.  
  357. } /* end assoc_lookup */
  358.  
  359.  
  360. mem_cell
  361. assocn_lookup( string, length, table)
  362.   register char* string;
  363.   register assoc_mem table;
  364. {
  365.   register entry retval;
  366.   int hashval;
  367.  
  368.   hashn(string, table->mask, length, &hashval);
  369.   
  370.   { register int  rehash = hashval;
  371.     register int * assoc_value = 0;
  372.     
  373.   look_at_slot:
  374.     {  entry * slot = &((*(table->array))[rehash]);
  375.        
  376.        assoc_value = *slot;
  377.        
  378.        if ( (assoc_value) == 0)
  379.      /* Nothing has hashed to this slot. 
  380.         Lookup has come to a sorry end. */
  381.      { 
  382.        return assoc_value;  /* <=========== */
  383.      }
  384.        
  385.        if (lstrncmp( string_from_cell(assoc_value,table), string, length) == 0)
  386.      /*  An identical string was previously put in this slot.
  387.      **  We found it!
  388.      */
  389.      { 
  390.        return assoc_value;  /* <=========== */
  391.      }
  392.        
  393.        /* collision... try next slot in the rehash orbit */
  394.        rehash = REHASH(rehash,table);
  395.        goto look_at_slot;        /* <=========== */
  396.  
  397.      }
  398.     
  399.   }
  400.  
  401.  
  402. } /* end assocn_lookup */
  403.  
  404.  
  405. mem_cell
  406. assocnf_lookup( string, length, table)
  407.   register char* string;
  408.   register assoc_mem table;
  409. {
  410.   register entry retval;
  411.   int hashval;
  412.  
  413.   hashnf(string, table->mask, length, &hashval);
  414.   
  415.   { register int  rehash = hashval;
  416.     register int * assoc_value = 0;
  417.     
  418.   look_at_slot:
  419.     {  entry * slot = &((*(table->array))[rehash]);
  420.        
  421.        assoc_value = *slot;
  422.        
  423.        if ( (assoc_value) == 0)
  424.      /* Nothing has hashed to this slot. 
  425.         Lookup has come to a sorry end. */
  426.      { 
  427.        return assoc_value;  /* <=========== */
  428.      }
  429.        
  430.        if (lstrncmpf( string_from_cell(assoc_value,table), string, length) == 0)
  431.      /*  An identical string was previously put in this slot.
  432.      **  We found it!
  433.      */
  434.      { 
  435.        return assoc_value;  /* <=========== */
  436.      }
  437.        
  438.        /* collision... try next slot in the rehash orbit */
  439.        rehash = REHASH(rehash,table);
  440.        goto look_at_slot;        /* <=========== */
  441.  
  442.      }
  443.     
  444.   }
  445.  
  446.  
  447. } /* end assocnf_lookup */
  448.  
  449.  
  450.  
  451.  
  452. /* This routine hashes a string and counts the number of chars in it.   */
  453. /* The hash value is a function of the table size.             */
  454.  
  455. static
  456. hash(string, mask, lenp, hashp)
  457.   char* string;
  458.   int mask;     /* Is table size minus one. */
  459.   int *lenp;
  460.   int *hashp;
  461.  
  462. {
  463.   register int len = 0;
  464.   register int hash = 0;
  465.   register char* cursor = string;
  466.  
  467.   len = 0;
  468.   hash = 0;
  469.  
  470.   while (*cursor)
  471.     { len++;
  472.       hash += ((hash << 1) + *cursor++);
  473.     }
  474.   *hashp = hash & mask;
  475.   *lenp = len;  
  476.  
  477.  
  478. } /* hash */
  479.  
  480. /* Like hash, except for counted (not null-terminated) strings */
  481. static
  482. hashn(string, mask, len, hashp)
  483.   char* string;
  484.   int mask;     /* Is table size minus one. */
  485.   int len;
  486.   int *hashp;
  487.  
  488. {
  489.   register int hash = 0;
  490.   register char* cursor = string;
  491.  
  492.   hash = 0;
  493.  
  494.   while (len--)
  495.     {
  496.       hash += ((hash << 1) + *cursor++);
  497.     }
  498.   
  499.   *hashp = hash & mask;
  500.  
  501. } /* hashn*/
  502. /* Like hash, except for counted (not null-terminated) strings */
  503. /* Folds cases. */
  504. static
  505. hashnf(string, mask, len, hashp)
  506.   char* string;
  507.   int mask;     /* Is table size minus one. */
  508.   int len;
  509.   int *hashp;
  510.  
  511. {
  512.   register int hash = 0;
  513.   register char* cursor = string;
  514.  
  515.   hash = 0;
  516.  
  517.   while (len--)
  518.     {
  519.       hash += ((hash << 1) + LOWER(*cursor));
  520.       cursor++;
  521.     }
  522.   
  523.   *hashp = hash & mask;
  524.  
  525. } /* hashn*/
  526.  
  527.  
  528.  
  529.  
  530. /* "Safe" memory allocation routine... zeros out memory obtained. */
  531.  
  532. static int*
  533. H_getmem(size)
  534.   int size;
  535. {
  536.   int * retval = (int*)calloc(size, sizeof(char));
  537.   if (retval == (int*)0)
  538.     {
  539.     fprintf(PANIC_FILE, "RESOURCE FAILURE: Assoc out of memory. (%d)\n",
  540.         size);
  541.     exitpg(1);
  542.     }
  543.   else return retval;
  544.  
  545. }
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552. /* When a table becomes half full, we double its size.  (The
  553. ** orbit of the rehash function touches exactly half the slots.)
  554. **
  555. */
  556.  
  557. static
  558. H_expand_table(table)
  559.   register assoc_mem table;
  560. {
  561.   register hash_table * old_table = table->array;
  562.   register int old_size = table->size;
  563.  
  564.   table->size_div_2 = table->size;
  565.   table->size = table->size * 2;
  566.   table->mask = table->size -1;
  567.  
  568.   table->array = (hash_table *)H_getmem(table->size * (sizeof (mem_cell)));
  569.  
  570.   /* Move the members from the old small table to be new big one. */
  571.   { register int recno;
  572.  
  573.     for (recno = 0; recno < old_size; recno++)
  574.       if ( (*old_table)[recno] != (entry)0)
  575.         H_reassoc( (*old_table)[recno], table );
  576.   }
  577.  
  578.   cfree (old_table);
  579. }
  580.  
  581.  
  582.  
  583.  
  584.  
  585. /* This routine is a little like assoc. It is used by expand_table() to
  586. ** put entries which were in table which overflowed into the new larger one.
  587. ** Used by assoc_free() to put entries back into the table which might
  588. ** have originally been bumped down the rehash orbit by the entry being
  589. ** removed.
  590. */
  591.  
  592. static
  593. H_reassoc( rec, table)
  594.   register entry rec;
  595.   register assoc_mem table;
  596. {
  597.   register entry retval;
  598.   register char* string = string_from_cell(rec, table);
  599.   int length;
  600.   int hashval;
  601.  
  602.   hash(string, table->mask, &length, &hashval);
  603.  
  604.   { register int  rehash = hashval;
  605.  
  606.     look_at_slot:
  607.     {  register entry * slot = &((*(table->array))[rehash]);
  608.  
  609.        if ( (*slot) == 0)
  610.         { 
  611.           *slot = rec;
  612.           *(mem_from_cell(*slot)) = rehash;
  613.  
  614.           return;        /* <========= */
  615.         }
  616.     
  617.        rehash = REHASH(rehash,table);
  618.        goto look_at_slot;      /* <========= */
  619.     }
  620.     
  621.   }
  622.  
  623.  
  624. } /* H_reassoc */
  625.  
  626.  
  627.  
  628.  
  629. /* This is a sequencer for a table.  You give it a pointer to an
  630. ** integer variable which you have initialized to zero, and it gives
  631. ** you a member of the table and modifies the variable.  Call it
  632. ** again without changing the variable and it gives you the next one, etc...
  633. **
  634. **    { int handle = 0;
  635. **      mem_cell member;
  636. **
  637. **      do { member = assoc_seq(table, &handle);
  638. **           if (member != NULL) process(member);
  639. **         }
  640. **      while (member != NULL);
  641. **    }
  642. **
  643. **
  644. ** DO NOT ADD OR DELETE MEMBERS BETWEEN RELATED CALLS TO assoc_seq().
  645. ** To do so will wreak non-determinancy if the assoc() caused the
  646. ** table to expand, or if the assoc_free() caused a member to be
  647. ** moved back up the rehash chain.  You might very easily miss some
  648. ** members.
  649. **
  650. */
  651.  
  652. mem_cell
  653. assoc_seq(table, num)
  654.   register assoc_mem table;
  655.   register int *num;
  656. {
  657.   register hash_table * hash_tab = table->array;
  658.   register int size = table->size;
  659.  
  660.   /* Standard linear search algorithm looks for next non-empty slot
  661.   ** at index *num or further down.
  662.   */
  663.   for (; (*num) < size; (*num)++)
  664.     if ( (*hash_tab)[*num] != (entry)0)
  665.     { mem_cell retval = (*hash_tab)[*num];
  666.       (*num)++;
  667.       return retval;
  668.     }
  669.  
  670.   return 0;
  671.  
  672. }
  673.  
  674.  
  675.  
  676. /*
  677. ** This procedure removes a member from a table.
  678. */
  679.  
  680. assoc_free(cell, table)
  681.   mem_cell cell;
  682.   assoc_mem table;
  683. {
  684.   /* Invariant: next_slot_num and next_slot point to entries in the rehash
  685.   ** orbit of the cell being removed.  They start out pointing to the
  686.   ** slot of the condemned cell itself:
  687.   */
  688.   register next_slot_num = *(mem_from_cell(cell));
  689.   register entry *next_slot = &((*(table->array))[next_slot_num]);
  690.  
  691.   /* Remove the condemned cell. */
  692.   free((char *)mem_from_cell(*next_slot));
  693.   *next_slot = 0; /* Splat! got it. */
  694.   table->entries -= 1;
  695.  
  696.   /* The entry we just removed might have caused some other entries
  697.   ** to be "bumped down the rehash orbit" when they were installed in
  698.   ** the table.  If that was the case, they can not now be found unless
  699.   ** they are moved to their (now) proper position in the table.
  700.   */
  701.    while(
  702.       next_slot_num = REHASH(next_slot_num,table),
  703.       next_slot = &((*(table->array))[next_slot_num]),
  704.       (*next_slot != 0)
  705.     )
  706.      { entry mover = *next_slot;
  707.        *next_slot = 0;
  708.        H_reassoc(mover, table);
  709.      }
  710.   
  711. }/* assoc_free */
  712.  
  713.  
  714.  
  715. /* Deletes a table and returns 0 if the table is empty.
  716. ** Does not delete table, but returns number of entries remaining if
  717. ** table is not empty.
  718. */
  719.  
  720. int
  721. assoc_mem_free(table)
  722.   assoc_mem table;
  723. {
  724.   if (table->entries == 0)
  725.     {  free((char *)table->array);
  726.        free((char *)table);
  727.        return 0;
  728.     }
  729.   else return table->entries;
  730.  
  731. }
  732.  
  733. /* Deletes all members in a table, then removes the table. */
  734.  
  735. assoc_mem_remove(table)
  736.   assoc_mem table;
  737. { register int num = 0;
  738.   register hash_table * hash_tab = table->array;
  739.   register int size = table->size;
  740.  
  741.   for (; (num) < size; (num)++)
  742.     if ( (*hash_tab)[num] != (entry)0)
  743.     { 
  744.           free((char *)mem_from_cell((*hash_tab)[num]));
  745.     }
  746.  
  747.  
  748.   free((char *)table->array);
  749.   free((char *)table);
  750.  
  751. }
  752.